// Backwards compatible version of: compiler.hooks.make.tapAsync()
(   compiler.hooks 
    ? 
    compiler.hooks.make.tapAsync('HtmlWebpackPlugin')
    : 
    compiler.plugin.bind(compiler, 'make')
    
    )(
        (compilation, callback) => {
    // Compile the template (queued)
        compilationPromise = childCompiler.compileTemplate(self.options.template, compiler.context, self.options.filename, compilation)
      .catch(err => {
        compilation.errors.push(prettyError(err, compiler.context).toString());
        return {
          content: self.options.showErrors ? prettyError(err, compiler.context).toJsonHtml() : 'ERROR',
          outputName: self.options.filename
        };
      })
      .then(compilationResult => {
        // If the compilation change didnt change the cache is valid
        isCompilationCached = compilationResult.hash && self.childCompilerHash === compilationResult.hash;
        self.childCompilerHash = compilationResult.hash;
        self.childCompilationOutputName = compilationResult.outputName;
        callback();
        return compilationResult.content;
      });
  });

  // Backwards compatible version of: compiler.plugin.emit.tapAsync()
  (compiler.hooks ? compiler.hooks.emit.tapAsync.bind(compiler.hooks.emit, 'HtmlWebpackPlugin') : compiler.plugin.bind(compiler, 'emit'))((compilation, callback) => {
    const applyPluginsAsyncWaterfall = self.applyPluginsAsyncWaterfall(compilation);
    // Get chunks info as json
    // Note: we're excluding stuff that we don't need to improve toJson serialization speed.
    const chunkOnlyConfig = {
      assets: false,
      cached: false,
      children: false,
      chunks: true,
      chunkModules: false,
      chunkOrigins: false,
      errorDetails: false,
      hash: false,
      modules: false,
      reasons: false,
      source: false,
      timings: false,
      version: false
    };
    const allChunks = compilation.getStats().toJson(chunkOnlyConfig).chunks;
    // Filter chunks (options.chunks and options.excludeCHunks)
    let chunks = self.filterChunks(allChunks, self.options.chunks, self.options.excludeChunks);
    // Sort chunks
    chunks = self.sortChunks(chunks, self.options.chunksSortMode, compilation);
    // Let plugins alter the chunks and the chunk sorting
    if (compilation.hooks) {
      chunks = compilation.hooks.htmlWebpackPluginAlterChunks.call(chunks, { plugin: self });
    } else {
      // Before Webpack 4
      chunks = compilation.applyPluginsWaterfall('html-webpack-plugin-alter-chunks', chunks, { plugin: self });
    }
    // Get assets
    const assets = self.htmlWebpackPluginAssets(compilation, chunks);
    // If this is a hot update compilation, move on!
    // This solves a problem where an `index.html` file is generated for hot-update js files
    // It only happens in Webpack 2, where hot updates are emitted separately before the full bundle
    if (self.isHotUpdateCompilation(assets)) {
      return callback();
    }

    // If the template and the assets did not change we don't have to emit the html
    const assetJson = JSON.stringify(self.getAssetFiles(assets));
    if (isCompilationCached && self.options.cache && assetJson === self.assetJson) {
      return callback();
    } else {
      self.assetJson = assetJson;
    }

    Promise.resolve()
      // Favicon
      .then(() => {
        if (self.options.favicon) {
          return self.addFileToAssets(self.options.favicon, compilation)
            .then(faviconBasename => {
              let publicPath = compilation.mainTemplate.getPublicPath({hash: compilation.hash}) || '';
              if (publicPath && publicPath.substr(-1) !== '/') {
                publicPath += '/';
              }
              assets.favicon = publicPath + faviconBasename;
            });
        }
      })
      // Wait for the compilation to finish
      .then(() => compilationPromise)
      .then(compiledTemplate => {
        // Allow to use a custom function / string instead
        if (self.options.templateContent !== undefined) {
          return self.options.templateContent;
        }
        // Once everything is compiled evaluate the html factory
        // and replace it with its content
        return self.evaluateCompilationResult(compilation, compiledTemplate);
      })
      // Allow plugins to make changes to the assets before invoking the template
      // This only makes sense to use if `inject` is `false`
      .then(compilationResult => applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-generation', false, {
        assets: assets,
        outputName: self.childCompilationOutputName,
        plugin: self
      })
    .then(() => compilationResult))
      // Execute the template
      .then(compilationResult => typeof compilationResult !== 'function'
      ? compilationResult
      : self.executeTemplate(compilationResult, chunks, assets, compilation))
      // Allow plugins to change the html before assets are injected
      .then(html => {
        const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName};
        return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-processing', true, pluginArgs);
      })
      .then(result => {
        const html = result.html;
        const assets = result.assets;
        // Prepare script and link tags
        const assetTags = self.generateHtmlTags(assets);
        const pluginArgs = {head: assetTags.head, body: assetTags.body, plugin: self, chunks: chunks, outputName: self.childCompilationOutputName};
        // Allow plugins to change the assetTag definitions
        return applyPluginsAsyncWaterfall('html-webpack-plugin-alter-asset-tags', true, pluginArgs)
          .then(result => self.postProcessHtml(html, assets, { body: result.body, head: result.head })
          .then(html => _.extend(result, {html: html, assets: assets})));
      })
      // Allow plugins to change the html after assets are injected
      .then(result => {
        const html = result.html;
        const assets = result.assets;
        const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName};
        return applyPluginsAsyncWaterfall('html-webpack-plugin-after-html-processing', true, pluginArgs)
          .then(result => result.html);
      })
      .catch(err => {
        // In case anything went wrong the promise is resolved
        // with the error message and an error is logged
        compilation.errors.push(prettyError(err, compiler.context).toString());
        // Prevent caching
        self.hash = null;
        return self.options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR';
      })
      .then(html => {
        // Replace the compilation result with the evaluated html code
        compilation.assets[self.childCompilationOutputName] = {
          source: () => html,
          size: () => html.length
        };
      })
      .then(() => applyPluginsAsyncWaterfall('html-webpack-plugin-after-emit', false, {
        html: compilation.assets[self.childCompilationOutputName],
        outputName: self.childCompilationOutputName,
        plugin: self
      }).catch(err => {
        console.error(err);
        return null;
      }).then(() => null))
      // Let webpack continue with it
      .then(() => {
        callback();
      });
  });